/***************************************************************************
													expr.cpp  -  description
														-------------------
		begin                : Tue Aug 27 2002
		copyright            : (C) 2002 by Michiel Visser
		email                : 
***************************************************************************/

/***************************************************************************
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
***************************************************************************/

#include "expr.h"

#ifdef DEBUGLOGGING
#define LOG_EXPR session.log(this)
#else
#define LOG_EXPR
#endif

namespace Expression
{
///.{

#define DEFPT(e) void e##Expr::printTyle() const { printf(#e); }

void Expr::evaluateToVoid()
{
	Object::Holder oh;
	evaluateToCopy(oh);
}
void Expr::evaluateToCopy(void *space)
{	evaluateToOriginal()->duplicateIn(space); // assume evaluateToOriginal never returns orphan
}
Object *Expr::evaluateToOriginal() // this is the only version of this function that will create orphan
{ return evaluateToCopy();
}
void ConstExpr::evaluateToCopy(void *space)
{ 
	session.m_curTypePtr=0;
	m_val->duplicateIn(space); //todo: check const expr does not apply to any sub & or #x
}
Object *SharedLiteralExpr::evaluateLiteralToCopy(void)
{ 
	session.m_curTypePtr=0;
	return session.m_sharedLiteralObjects[m_index];
}

Object *SharedLiteralExprAssigned::evaluateLiteralToCopy(void)
{ 
	Object *r=session.m_sharedLiteralObjects[m_index]=ao();
	a->evaluateLiteralToCopy(r);
	session.m_curTypePtr=0;
	return r;
}

//todo : change literaltoCopies to evaluateToOriginal? this means list and maps should use evalutetooriginal

void SharedLiteralExpr::toArray(OList::P array) const {
	ADDSTR("#");
	char s[14];
	sprintf(s,"%i ",m_index+1);
	ADDSTR(s);
}

void SharedLiteralExprAssigned::toArray(OList::P array) const {
	OList::P b=array;
	ADDSTR("= ");
	ADDA(a->toArrayObject());
	SharedLiteralExpr::toArray(b);
}

void SharedLiteralsScopeExpr::toArray(OList::P array) const {
	m_child->toArray(array);
}

SharedLiteralsScopeExpr::SharedLiteralsScopeExpr(EPtr c, int sharedLiteralCount): m_child(c), m_sharedLiteralCount(sharedLiteralCount)
{
}

void SharedLiteralsScopeExpr::init(void)
{
	session.m_sharedLiteralObjects.growByRealloc(m_sharedLiteralCount);
}

void SharedLiteralsScopeExpr::finish(void)
{
	session.m_sharedLiteralObjects.erase();
}

void ReferenceExpr::evaluateLiteralToCopy(void *space)
{
	new (space)  RefObject(a->evaluateLiteralToCopy());
}

void SharedLiteralsScopeExpr::evaluateToCopy(void *space) { init(); m_child->evaluateLiteralToCopy(space); finish(); }

void Expr::evaluateLiteralToCopy(void *space)  { _ASSERT(0); } //should never happen
Object *Expr::evaluateLiteralToCopy()  { Object *o=ao(); evaluateLiteralToCopy(o); return o;}

///.}

void ConstExpr::evaluateLiteralToCopy(void *space)
{
	Expr::evaluateToCopy(space);
}

void Expr::toExprArray(OList::P array) const
{
	ADDSTR("? ");
	ADDA(toArrayObject(levelModify));
}

void FuncExpr::toExprArray(OList::P array) const
{
	ADDSTR(toTyle());
}

Expr::~Expr() {}

void Expr::doStackOperation(void *space, Object *var, TypeVar s)
{ 
	session.pushStack(var,s);
	evaluateToCopy(space);
	session.popStack();
}

void ActAbstractExpr::evaluateToCopy(void *space)
{ 
	EPtr e;
	Object *o=a->evaluateToPersistCopy();
	new (e) ActOnExpr(o->makeExpr(),b);
	new (space) ExprObject(e);
}

#define DEF_T(name) void name##Expr::evaluateToCopy(void *space)
DEF_T(Negate     ) 
{ {
		Object::Holder oha;
		a->evaluateToPersistCopy(oha);
		oha->valueCopy(space);
		((Object *)space)->negate(); 
	}
	session.m_curTypePtr = 0;
}
DEF_T(Inverse    ) { Object::Holder oha; a->evaluateToCopy(oha); new (space)  IntegerObject(!oha->toInt()); session.m_curTypePtr = 0;}
DEF_T(Length     ) { Object::Holder oha; a->evaluateToCopy(oha); new (space)  IntegerObject(oha->length()); session.m_curTypePtr = 0;}
DEF_T(Type       ) { Object::Holder oha; a->evaluateToCopy(oha); new (space)  IntegerObject(oha->getTypeNum()); session.m_curTypePtr = 0;}
DEF_T(BitNot     ) 
{ { Object::Holder oha; a->evaluateToPersistCopy(oha); oha->valueCopy(space); 
		((Object *)space)->bitNot();  
	}
	session.m_curTypePtr = 0;
}
DEF_T(Describe   ) 
{ 
	EPtr e; 
	Object::Holder oh;
	a->evaluateToCopy(oh); //else #[num]'s are lost
	new (space)  ExprObject(oh->makeExpr());
	session.m_curTypePtr = 0;
}
DEF_T(Value      ) 
{ 
	Object::Holder oha; a->evaluateToCopy(oha); oha->valueCopy(space); 
}
DEF_T(Reference  ) { new (space)  RefObject(a->evaluateToOriginal()); }

Object *DeReferenceExpr::evaluateToOriginal() 
{ 
	return a->evaluateToOriginal()->deReference();
}
void DeReferenceExpr::evaluateToCopy(void *space)
{
	Object::Holder oa;	
	a->evaluateToCopy(oa);
	Object *d(oa->deReference());
	d->duplicateIn(space);
}

Object *      ValueExpr::evaluateToOriginal() 
{ 
	return a->evaluateToOriginal()->value(); 
}

inline void RemoveAt(void *space, Object *a, Object *b)   { a->removeAt(b,space); }
inline void ShiftLeft(void *space, Object *a, Object *b)  { a->shiftLeft(b,space); }
inline void ShiftRight(void *space, Object *a, Object *b) { a->shiftRight(b,space); }
inline void Sub(void *space, Object *a, Object *b)        { new (space) IntegerObject(a->sub(b)?1:0); }
inline void Mul(Object *a, Object *b)        { a->mul(b); }
inline void Add(Object *a, Object *b)        { a->add(b); }
inline void Prepend(Object *a, Object *b)    { a->prepend(b);}
inline void Mod(Object *a, Object *b)
{ if (a->mod(b))
	{	ChildrenPainter cpb(b);
		session.reportRuntimeError("Modulation by zero."); 
	}
}
inline void Div(Object *a, Object *b)
{ if(!a->div(b)) 
	{	ChildrenPainter cpb(b);
		session.reportRuntimeError("Division by zero."); 
	}
}

inline void Equal(void *space, Object *a, Object *b)      { new (space) IntegerObject(*a==b?1:0); }
inline void Unequal(void *space, Object *a, Object *b)    { new (space)  IntegerObject(*a!=b?1:0); }
inline void Smaller(void *space, Object *a, Object *b)      { new (space)  IntegerObject(a->compare(b)<0); }
inline void SmallerEqual(void *space, Object *a, Object *b) { new (space)  IntegerObject(a->compare(b)<=0); }
inline void Greater(void *space, Object *a, Object *b)       { new (space)  IntegerObject(a->compare(b)>0); }
inline void GreaterEqual(void *space, Object *a, Object *b)  { new (space)  IntegerObject(a->compare(b)>=0); }
inline void BitAnd(Object *a, Object *b)  { a->bitAnd(b);}
inline void BitOr(Object *a, Object *b)   { a->bitOr(b);}
inline void BitXor(Object *a, Object *b)  { a->bitXor(b);}
#define DEF_EVAL_OF_EXEC(F) \
void F##Expr::evaluateToCopy(void *space) { \
	ChildrenPainter cp;\
	{	{	Object::Holder oha;\
			a->evaluateToCopy(oha);\
			oha->valueCopy(space);\
			cp.set((Object *)space); \
		} \
		Object::Holder ohb;\
		{	Object::Holder ohr;\
			b->evaluateToCopy(ohb);\
			F(ohr,(Object *)space,ohb); \
		}\
	}\
	session.m_curTypePtr=0;\
} \
void F##AssignExpr::evaluateToCopy(void *space) { \
	Object *oha(a->evaluateToOriginal());\
	{	ChildrenPainter cpa(oha); \
		{	Object::Holder ohb;\
			b->evaluateToCopy(ohb);\
			F(space, oha, ohb); \
		}\
	}\
	session.m_curTypePtr = 0;\
}

#define DEF_EVAL_OF_VOID_EXEC(F) \
void F##Expr::evaluateToCopy(void *space) \
{ ChildrenPainter cp; \
	{ {	Object::Holder oha;\
			a->evaluateToCopy(oha);\
			oha->valueCopy(space); \
			cp.set((Object *)space); \
		}\
		Object::Holder ohb; \
		b->evaluateToCopy(ohb);\
		F((Object *)space,ohb); \
	} \
	session.m_curTypePtr = 0; \
} \
void F##AssignExpr::evaluateToCopy(void *space) \
{ evaluateToVoid(); new (space) NullObject(); \
}\
void F##AssignExpr::evaluateToVoid() \
{ Object *oha(a->evaluateToOriginal());\
	{	SelfPainter cpa(oha); \
		{	Object::Holder ohb;\
			b->evaluateToCopy(ohb);\
			F(oha,ohb); \
		}\
	} \
}

// this one must never return childs of operands:
#define DEF_EVAL_OF_CALC(F) \
void F##Expr::evaluateToCopy(void *space) \
{ Object::HolderCP oha;\
	a->evaluateToCopy(oha);\
	{	Object::Holder ohb;\
		oha.protectChilds(); \
		{	b->evaluateToCopy(ohb);\
			F(space, oha, ohb);\
		}\
	} \
	session.m_curTypePtr = 0; \
}

DEF_EVAL_OF_VOID_EXEC(BitAnd)
DEF_EVAL_OF_VOID_EXEC(BitOr)
DEF_EVAL_OF_VOID_EXEC(BitXor)
DEF_EVAL_OF_VOID_EXEC(Mul)
DEF_EVAL_OF_VOID_EXEC(Add)
DEF_EVAL_OF_VOID_EXEC(Prepend)
DEF_EVAL_OF_VOID_EXEC(Mod)
DEF_EVAL_OF_VOID_EXEC(Div)

DEF_EVAL_OF_EXEC(RemoveAt)
DEF_EVAL_OF_EXEC(ShiftLeft)
DEF_EVAL_OF_EXEC(ShiftRight)
DEF_EVAL_OF_EXEC(Sub)
DEF_EVAL_OF_CALC(Equal)
DEF_EVAL_OF_CALC(Unequal)
DEF_EVAL_OF_CALC(Smaller)
DEF_EVAL_OF_CALC(SmallerEqual)
DEF_EVAL_OF_CALC(Greater)
DEF_EVAL_OF_CALC(GreaterEqual)

void FindIndexExpr::evaluateToCopy(void *space)
{
	Object::HolderCP oha;
	a->evaluateToCopy(oha);
	{	oha.protectChilds();
		{	Object::Holder ohb;
			b->evaluateToCopy(ohb);
			oha->findIndex((Object *)ohb,space); // finished?? todo: fix problem with findindex (see implementation)
		}
	}
	// m_curTypePtr is already set by findIndex
}

void InsertAtExpr::evaluateToCopy(void *space)
{ 
	ChildrenPainter cp;
	{
		{	Object::Holder oha;
			a->evaluateToCopy(oha);
			oha->valueCopy(space);
			cp.set((Object *)space); // prevent children of result to disappear when ohb is released. do this whenever subexpressions can be encapsulated in result
		}
		Object::Holder ohb;
		b->evaluateToCopy(ohb);
		((Object *)space)->insertAt(ohb,NullObject::obj());
	}
	session.m_curTypePtr = 0;
}

Object *InsertAtAssignExpr::evaluateToOriginal() 
{ Object *r;
	SelfPainter cpr;
	{	Object *oha(a->evaluateToOriginal());
		TypeVar *p = session.m_curTypePtr;
		{	SelfPainter cpa(oha);
			{	Object::Holder ohb;
				b->evaluateToCopy(ohb);
				r=NullObject::obj();
				cpr.set(r);
				oha->insertAt(ohb,r);
			}
		}
		session.m_curTypePtr = p;
	}
	return r;
}

void InsertAtAssignExpr::evaluateToVoid() {
	Object *oha(a->evaluateToOriginal());
	{	
		SelfPainter cpa(oha);
		{	
			Object::Holder ohb;
			b->evaluateToCopy(ohb);
			oha->insertAt(ohb,NullObject::obj());
		}
	}
}

void AndExpr::evaluateToCopy(void *space)
{
	a->evaluateToCopy(space);
	if (((Object *)space)->toInt()) {
		((Object *)space)->~Object();
		b->evaluateToCopy(space);
	}
	session.m_curTypePtr = 0;
}

void OrExpr::evaluateToCopy(void *space)
{ a->evaluateToCopy(space);
	if (!((Object *)space)->toInt())
	{	((Object *)space)->~Object();
		b->evaluateToCopy(space);
	} 
	session.m_curTypePtr = 0;
}

void AndAssignExpr::evaluateToVoid()
{
	Object *ao(a->evaluateToOriginal());
	if (ao->toInt() )
	{ ((Object *)ao)->~Object();
		b->evaluateToCopy(ao);
	}
}

void OrAssignExpr::evaluateToVoid()
{ Object *ao(a->evaluateToOriginal());
	if (!ao->toInt() )
	{ ((Object *)ao)->~Object();
		b->evaluateToCopy(ao);
	} 
}

void AndAssignExpr::evaluateToCopy(void *space) { evaluateToVoid(); new (space) NullObject(); }
void OrAssignExpr::evaluateToCopy(void *space) { evaluateToVoid(); new (space) NullObject(); }
void InsertAtAssignExpr::evaluateToCopy(void *space) {	evaluateToVoid(); new (space) NullObject(); } 
void SwitchOnExpr::evaluateToCopy(void *space)
{ int i;
	{	Object::Holder oha;
		a->evaluateToCopy(oha);
		i=((Object *)oha)->toInt() + 1;
	}
	EPtr *o=m_choices.pnext(i);
	if (o && !!*o) { 
	IFDEBUGDEEP( (*o)->printTyle(); )
		(*o)->evaluateToCopy(space);//: NullObject::obj();
	}
	else if (!!*m_choices && ( i>1 || !!m_choices.next() ) ) {
		IFDEBUGDEEP( (*m_choices)->printTyle(); )
		(*m_choices)->evaluateToCopy(space);
	}
	else new (space) NullObject();
}

IFDEBUG( 
void SwitchOnExpr::printTyle() const { printf("SwitchOn: "); a->printTyle(); }
)

void SwitchOnExpr::toArray(OList::P array) const
{
	ADDA(a->toArrayObject(levelSwitch));
	ADDSTR(" ? ");
	bool first=true;
	FOREACH(EList, m_choices, i)
	{ if (first) first=false; 
		else ADDSTR(" : ");
		if (!!*i) 
		{	ADDA( (*i)->toArrayObject(levelSwitch) );
		}
	}
}

void ListExpr::evaluateToCopy(void *space)
{ 
	ChildrenPainter cp;
	IFDEBUGDEEP(printf("{\n");)
	{
		push();
		OList::P resi(&(new (space) ListObject())->getEditableListQuick());
		cp.set((Object *)space);
		FOREACH(List<Elem>, m_elements, i)	{ 
			IFDEBUGDEEP( printf("le:  "); i->elem->printTyle(); printf("\n"); )
			if ( i->keepResult ) { resi->prepend(i->elem->evaluateToPersistCopy()); resi++; }
			else i->elem->evaluateToVoid();
		}
		pop();
	}
	session.m_curTypePtr=0;
	IFDEBUGDEEP(printf("}\n");)
}

void ListExpr::evaluateLiteralToCopy(void *space) // only for constants!
{
	OList::P resi(&(new (space) SelfishListObject())->getEditableListQuick());
	FOREACH(List<Elem>, m_elements, i) { 
		resi->prepend(i->elem->evaluateLiteralToCopy()); 
		resi++;
	}
}

IFDEBUG( DEFPT(List)  )

void ListExpr::toArray(OList::P array) const
{ if (!m_elements)
	{	ADDSTR("{");
		ADDSTR("}");
	}
	else
	{
		ADDSTR("{ ");
		bool first=true;
		FOREACH(List<Elem>, m_elements, i) {
			if (first) first=false; else ADDSTR(", ")
			ADDA(i->elem->toArrayObject());
			if (!i->keepResult) { first=true; ADDSTR("; "); }
		}
		if (first) ADDSTR("}") else ADDSTR(" }");
	}
}
BlockExpr::~BlockExpr() { if (!!m_queue) ((LocalVarVectorQueue &)*m_queue).erase() ; }

void SequenceExpr::evaluateToCopy(void *space)
{ 
	push();
	if (keepResult)
	{ if (!!m_elements)
		{
			EList::Iter i(m_elements);
			for(;;)
			{	EList::Iter oi(i.next());
				if (!oi) break;
				IFDEBUGDEEP( printf("void:  "); (*i)->printTyle(); printf("\n"); )
				(*i)->evaluateToVoid();
				i=oi;
			}
			IFDEBUGDEEP( printf("copy:  "); (*i)->printTyle(); printf("\n"); )
			(*i)->evaluateToCopy(space);
			pop(); //todo: check this pop does not free, else protect 'space's children
			return;
		}
	}
	else
	{	EList::Iter i(m_elements);
		FOREACH(EList, m_elements, i)
		{ IFDEBUGDEEP( printf("void:  "); (*i)->printTyle(); printf("\n"); )
			(*i)->evaluateToVoid();
		}
	}
	pop();
	new (space) NullObject();
}

IFDEBUG( DEFPT(Sequence)  )
void SequenceExpr::toArray(OList::P array) const
{ if (!m_elements) ADDSTR("()")
	else
	{
		ADDSTR("( ")
		bool first=true;
		FOREACH(EList, m_elements, i)
		{ if (first) first=false; else ADDSTR("; ")
			ADDA((*i)->toArrayObject())
		}
		if (!first && !keepResult) ADDSTR(";")
		ADDSTR(" )")
	}
}

void WhileExpr::evaluateToCopy(void *space)
{
	OList::P resi(&(new (space) ListObject())->getEditableListQuick());
	ChildrenPainter cp((Object *)space);
	Object::HolderNotDeleting bo;
	while(true) { 
		Object::Holder oha;
		a->evaluateToCopy(oha);
		if (oha->toInt()==0) break;
		b->evaluateToCopy(bo);
		Object::insertConditionally(bo,resi);
	}
	session.m_curTypePtr=0;
	objectGC.checkGarbage();
}

void ActOnMultiExpr::evaluateToCopy(void *space)
{ 
#ifdef TYLE_V2
/*
UPDATE: neem $~ en $$ als running variables, dus
als iemand ze wijzigt, pech gehad.
wel moeten we steeds $~ netjes overschrijven, dus assignen
voor $$ kunnen we gebruikmaken van een listPTr, en $$ is selfishList.
eerste elem wijst aan $~, en tweede elem wijst naar $, steeds opnieuw

dit moet kunnen?
x:!($~ = 4;), want
x:!<k~v>(k=4;) moet ook kunnen
en what about:
x:!($$=5;)
zou eigenlijk ook moeten kunnen anders is het de enige uitzondering.
dit moet ook kunnen:
ctup(3):! &$$, moet {&{0,0},&{1,1},&{2,2}} opleveren
... ( $=5; $$ ) ... moet $$ mag nu {..,5} geven, totdat iemand $$ overschijft met andere list of iets anders
DUS: aan het begin is $$ selfishList van {$~, $}, maar de OList geshared wordt! Hmm, dat laatste kan niet, want dan worden de elems ook gecopieerd
hoe te doen: de origeel moet selfishlist zijn als $$ precies {$~,$} is

push {index,value}
push index

*/
#endif
 {
	OList::P resi(&(new (space) ListObject())->getEditableListQuick());
	ChildrenPainter cpr((Object *)space);
	{
		{ 
			Object::Holder oha;
			a->evaluateToPersistCopy(oha);
#ifdef TYLE_V2
			oha->actOnElems(resi,b);
#else
			{	
				TypeVar strct;
				OListPtr lp(oha->getIterList(strct));
				{	
					OListChildrenPainter ocp(*lp);
					Object::HolderNotDeleting bo;
					FOREACH(OList, *lp, i) { 
						b->doStackOperation(bo,*i,strct);
						Object::insertConditionally(bo,resi);
					}
				}
			}
#endif 
		}
		session.m_curTypePtr=0;
		objectGC.checkGarbage();
	}
  }
}

void ActOnColumnsExpr::evaluateToCopy(void *space)
{
	//TODO: put code here of list::actOnColumns
	OList::P resi(&(new (space) ListObject())->getEditableListQuick());
	ChildrenPainter cpr((Object *)space);
	{ 
		Object::HolderCP oha;
		a->evaluateToPersistCopy(oha);
		oha.protectChilds();
		oha->actOnColumns(resi,b);
	}
	session.m_curTypePtr=0;
	objectGC.checkGarbage();
}

void IndexQueryExpr::evaluateToCopy(void *space)
{ 
	OList::P resi(&(new (space) ListObject())->getEditableListQuick());
	ChildrenPainter cpr((Object *)space);
	{
		Object::Holder oha;
		a->evaluateToPersistCopy(oha);
		{	int index=0;
			TypeVar strct;
			OListPtr lp(oha->getIterList(strct));
			{	
				OListChildrenPainter ocp(*lp);
				FOREACH(OList, *lp, i) {
					Object::Holder ohb;
					b->doStackOperation(ohb,*i,strct); //todo clean
					if (ohb->toInt()!=0) { resi->prepend(NEW_OBJ IntegerObject(index)); resi++; }
					index++;
				}
			}
		}
	}
	session.m_curTypePtr=0;
	objectGC.checkGarbage();
}
#if _DEBUG_ENABLE==1
#define CHECKGARBAGE_ON_CALL
#endif
void ApplyExpr::evaluateToCopy(void *space)
{
	Object::HolderCP ohb;
	b->evaluateToCopy(ohb);
	TypeVar *oldType = session.m_curTypePtr;
	ohb.protectChilds();
	ohb->apply(a,space);
	session.m_curTypePtr = oldType;
#ifdef CHECKGARBAGE_ON_CALL
	ChildrenPainter cpr((Object *)space);
	objectGC.checkGarbage(); 
#endif
}

void ApplyExpr::evaluateToVoid()
{
	{
		Object::HolderCP ohb;
		b->evaluateToCopy(ohb);
		ohb.protectChilds();
		ohb->applyToVoid(a);
	}
#ifdef CHECKGARBAGE_ON_CALL
	objectGC.checkGarbage(); 
#endif
}

inline void transferType(TypeVar *t) {
	if (session.m_curTypePtr && t && t!=session.m_curTypePtr && !*t && !!*session.m_curTypePtr)
		*t = *session.m_curTypePtr;
}
void AssignmentExpr::evaluateToCopy(void *space)
{
	execute()->duplicateIn(space);
}

Object * AssignmentExpr::execute()
{ 
	Object *ar(a->evaluateToOriginal());
	{
		SelfPainter spar(ar); // this in case we have a on-the-fly created reference. todo: if we don't have this anymore in future, get rid of this
		TypeVar *t=session.m_curTypePtr;
		if (ar->vtable==&vtable_Char) {
			Object::Holder h;
			b->evaluateToCopy(h);
			*(((CharObject *)ar)->m_char)->m_ptr=h->toString()[0];
		} else {
			Object::HolderNotDeleting h; //we cannot assign directly to 'ar', because 'ar' might be used in 'b'.
			b->evaluateToPersistCopy(h);
			ar->~Object();
			memcpy(ar,(Object *)h,OBJ_SIZE);
		}
		transferType(t);
	}
	return ar;
}

void AssignmentExpr::evaluateToVoid()
{
	execute();
}

void ActOnExpr::evaluateToCopy(void *space)
{ 
	Object *ao(a->evaluateToPersistCopy());
	{	
		SelfPainter spa(ao);
		b->doStackOperation(space, ao, session.getCurType() ); 
	}
#ifdef CHECKGARBAGE_ON_CALL
	ChildrenPainter cpr((Object *)space);
	objectGC.checkGarbage(); 
#endif
	session.m_curTypePtr=0;
}

String Expr::toTyle() const
{ 
	Object::Holder oh;
	toArrayObject(oh);
	return oh->toString();
}
IFDEBUG( DEFPT() )
void Expr::toArray(OList::P array) const {}
void Expr::toArrayObject(void *space) const
{	
	OList ol;
	toArray(&ol);
	new (space) ListObject(ol);
}

Object *Expr::toArrayObject() const
{ 
	Object *p=ao(); toArrayObject(p); return p; 
}

Object *Expr::toArrayObject(int parentlevel) const
{
	OList ol;
	toArray(&ol);
	Object *o=NEW_OBJ ListObject(ol);
	if (level()>parentlevel)
	{
		OList pol;
		pol.prepend(NEW_OBJ StringObject(" )"));
		pol.prepend(o);
		pol.prepend(NEW_OBJ StringObject("( "));
		return NEW_OBJ ListObject(pol);
	}
	return o;
}

int HeadStructIndexingExpr::level() const { return levelHeadIndex; }

IFDEBUG( DEFPT(Const) )
void ConstExpr::toArray(OList::P array) const
{ m_val->toArray(array);
}

Object *DollarBasicExpr::evaluateToOriginal()
{	StackVar &v=session.getStackVar(m_depth);
	session.m_curTypePtr = v.m_typePtr;
	return v.val;
}
#ifdef TYLE_V2
Object *DollarVarFirstFromMultipleExpr::evaluateToOriginal()
{	StackVar &v=session.getStackVar(0);
	TypeVar *t = v.m_typePtr;
	session.m_curTypePtr = t && !!*t ? &(*t)->m_childType : 0;
	return v.val->index(0);
}
#if 0
Object *DollarVarMultiFromSingleExpr::evaluateToOriginal()
{		StackVar &v=session.getStackVar(0);
	//session.m_curTypePtr = v.m_typePtr; 
	todo: how to type?
	OListPtr l;
	(new (l) OList())->prepend(v.val);
	Object *r=NEW_OBJ SelfishListObject(l);
	return r;
}
#endif
#endif

IFDEBUG( DEFPT(DollarVar) )
void DollarVarExpr::toArray(OList::P array) const
{	char s[3]="$"; 
	s[1]=((char *)&str)[0];
	s[2]=((char *)&str)[1];
	ADDSTR(s);
}
IFDEBUG(void ApplyExpr::printTyle() const { 
	this->b->printTyle(); printf("("); this->a->printTyle(); printf(")");
})
void ApplyExpr::toArray(OList::P array) const
{	
	ADDA(b->toArrayObject(levelIndex))
	if ( a->level()!=levelBlock )
	{	OList ol;
		ol.prepend(NEW_OBJ StringObject(" )"));
		ol.prepend(a->toArrayObject());
		ol.prepend(NEW_OBJ StringObject("( "));
		ADDA(NEW_OBJ ListObject(ol));
	}
	else
		ADDA(a->toArrayObject())
}

IFDEBUG( DEFPT(StructName) )
void StructNameExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR("[]");
}
void StructNameExpr::evaluateToCopy(void *space)
{
	{
		Object::Holder oh;
		a->evaluateToCopy(oh); // not to void, because void does keep type
	}
	TypeVar p(session.getCurType());
	if (!p) new (space) NullObject();
	else new (space) StringObject(p->m_name);
}
IFDEBUG( DEFPT(StructElems) )
void StructElemsExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR(".*");
}
void StructElemsExpr::evaluateToCopy(void *space)
{
	{
		Object::Holder oh;
		a->evaluateToCopy(oh); // not to void, because void does keep type
	}
	TypeVar p(session.getCurType());
	if (!p) new (space) NullObject();
	else
	{ OList l;
		p->getElemNames(l);
		new (space) ListObject(l);
	}
}

IFDEBUG( DEFPT(Eval) )
void EvalExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR(" !");
}
void EvalExpr::evaluateToCopy(void *space)
{
	TypeVar *t;
	{
		Object::HolderCP oha;
		a->evaluateToCopy(oha);
		oha.protectChilds();  // because it can be a refObject referencing a exprObject!
		t=session.m_curTypePtr;
		oha->evaluateToCopy(space);
	}
	session.m_curTypePtr=t;
}
void EvalExpr::evaluateToVoid()
{
	Object::HolderCP oha;
	a->evaluateToCopy(oha);
	oha.protectChilds();  // because it can be a refObject referencing a exprObject!
	oha->evaluateToVoid();
}

void IndexExpr::evaluateToCopy(void *space)
{
	TypeVar *t;
	{	Object::HolderCP oha;
		a->evaluateToCopy(oha);
		t=session.m_curTypePtr;
		{	oha.protectChilds();
			Object::Holder ohb;	    
			b->evaluateToCopy(ohb);
			oha->indexCopy(ohb,space);
		}
	}
	if (t)
	{	if (!!*t)
			session.m_curTypePtr = &(*t)->m_childType;
		else
			session.m_curTypePtr = 0;
	}
}

void IndexAltExpr::evaluateToCopy(void *space)
{	
	{	Object::HolderCP oha;
		a->evaluateToCopy(oha);
		TypeVar *t=session.m_curTypePtr;
		{	oha.protectChilds();
			Object::Holder ohb;	    
			b->evaluateToCopy(ohb);
			Object *_this=oha;
			while(_this->vtable == &vtable_Ref) _this=M_OBJ;
			int tn=_this->getTypeNum();
			session.m_curTypePtr = 0;
			if ( tn == TOT_Map)
			{ if (t && !!*t)
				{	session.m_curTypePtr = (*t)->getKeyPtr();
				}
				int i=ohb->toInt();
				if (i>=0)
				{	FOREACHM(O_MAP->, { if (!i) { p->key()->duplicateIn(space); goto done; }; i--;} )
				}
				else
				{	FOREACHM_BACK(O_MAP->, { i++; if (!i) { p->key()->duplicateIn(space); goto done; }; } )
				}
			}
			else if ( tn == TOT_List)
			{ int idx=0;
				FOREACH(OList, O_LIST->getList(), i)
				{	if (**i==ohb)
					{	new (space) IntegerObject(idx);
						goto done;
					}
					idx++;
				}
			}
			else if ( tn == TOT_String)
			{	Object *b = ohb->value();
				String as=_this->toString();
				if (b ->getTypeNum() == TOT_RegExp )
				{		List<RE::Match> matches;
						((RegExpObject *)b)->m_expr->match(&matches,as,as.length(),true);
						t = session.m_curTypePtr = 0;
						if (!!matches)
						{
							OList m;
							{	OList regs;
								OList::P regsi(&regs);
								FOREACH(List<String>, matches->m_regs,j)
								{	regsi->prepend(NEW_OBJ StringObject(*j));
									regsi++;
								}
								m.prepend(NEW_OBJ ListObject(regs));
							}
							m.prepend(NEW_OBJ IntegerObject(matches->m_len));
							m.prepend(NEW_OBJ IntegerObject(matches->m_pos));
							new (space) ListObject(m);
							session.m_curTypePtr=&RegExpResultType->m_childType;
							goto done;
						}
						//todo: set type
						//todo: make one function for adding regexp output item
				}		
				else
				{
					String bs=b->toString();
					char *res=strstr((char *)as,(char *)bs);
					if (res)
					{	new (space) IntegerObject(res-as);
						goto done;
					}
				}
			}
			new (space) NullObject(); 
		}
	}
done:
	return;
}

Object *IndexExpr::evaluateToOriginal()
{	
	Object *ao(a->evaluateToOriginal());
	TypeVar *t=session.m_curTypePtr;
	Object *i;
	{	SelfPainter cpa(ao);
		{	Object::Holder ohb;	    
			b->evaluateToCopy(ohb);
			i=ao->index(ohb);
		}
	}
	if (t)
	{	if (!!*t)
			session.m_curTypePtr = &(*t)->m_childType;
		else
			session.m_curTypePtr = 0;
	}
	return i;
}

IFDEBUG( DEFPT(Index) )
void IndexExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR("[");
	ADDA(b->toArrayObject())
	ADDSTR("]");
}

IFDEBUG( DEFPT(IndexAlt) )
void IndexAltExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR("[:");
	ADDA(b->toArrayObject())
	ADDSTR("]");
}

//todo: mapnode always initialized key, maybe also put mapnodes in allocator
void MapExpr::evaluateToCopy(void *space)
{	MapObject *m=new (space) MapObject();
	{	ChildrenPainter cp((Object *)space);
		{
			FOREACH(List<EPtr>, m_elements, i)
			{
				Object::Holder oha;
				(*i)->evaluateToPersistCopy(oha);
				i++;
				if (!*i)
					m->insertAt((Object *)oha, NullObject::obj());
				else
				{ ChildrenPainter cpa(oha);
					m->insertAt((Object *)oha, (*i)->evaluateToPersistCopy());
				}
			}
		}
	}
}

void MapExpr::evaluateLiteralToCopy(void *space)
{	
	MapObject *m=new (space) SelfishMapObject();
	FOREACH(List<EPtr>, m_elements, i)
	{
		Object::Holder oha;
		(*i)->evaluateLiteralToCopy(oha);
		i++;
		if (!*i)
			m->insertAt((Object *)oha, NullObject::obj());
		else {
			m->insertAt((Object *)oha, (*i)->evaluateLiteralToCopy());
		}
	}
}

IFDEBUG( DEFPT(Map) )
void MapExpr::toArray(OList::P array) const
{	ADDSTR("{: ");
	bool first=true;
	FOREACH(List<EPtr>, m_elements, i)
	{ 
		if (first) { first=false; } else { ADDSTR(", "); }
		OList pair;
		pair.prepend((*i)->toArrayObject());
		i++;
		if (!!*i) {
			pair.end().prepend(NEW_OBJ StringObject(" => "));
			pair.end().prepend((*i)->toArrayObject());
		}
		ADDA(NEW_OBJ ListObject(pair));
	}
	ADDSTR(" }");
}

void BlockVarExpr::evaluateToCopy(void *space)
{	
	LOG_EXPR;
	VarValue &vec = ((LocalVarVectorQueue &)*queue).getVarValue(number);
	session.m_curTypePtr = vec.m_typeVarPtr;
	vec.m_value->duplicateIn(space);
}
Object *BlockVarExpr::evaluateToOriginal()
{ 
	LOG_EXPR;
	VarValue &vec = ((LocalVarVectorQueue &)*queue).getVarValue(number);
	session.m_curTypePtr = vec.m_typeVarPtr;
	return vec.m_value;
}
IFDEBUG(void BlockVarExpr::printTyle() const { printf(".%s",(char *)((LocalVarVectorQueue &)*queue).getName(number)); })
IFDEBUG(void SharedLiteralsScopeExpr::printTyle() const { printf("SharedLiterals:"); m_child->printTyle(); })
IFDEBUG(void SharedLiteralExpr::printTyle() const { printf("#%i\n",m_index+1); })
IFDEBUG(void SharedLiteralExprAssigned::printTyle() const { printf("#%i=...\n",m_index+1); })

void BlockVarExpr::toArray(OList::P array) const
{	if (session.m_annotate)
		ADDSTR("/*l*/");
	ADDSTR(((LocalVarVectorQueue &)*queue).getName(number));
}
IFDEBUG(void LocalBlockVarExpr::printTyle() const { BlockVarExpr::printTyle(); })
void LocalBlockVarExpr::toArray(OList::P array) const
{	BlockVarExpr::toArray(array);
	ADDSTR(".")
}
Object *GlobalVarExpr::evaluateToOriginal()
{ 
	LOG_EXPR;
	session.m_curTypePtr = &m_v.getType();
	return m_v.m_value;
}

Object *GlobalVarInitialExpr::evaluateToOriginal()
{ 
	GlobalVariable *g=session.getGlobalVar(m_name);
	g->expr = EPtr(this);
	this->~GlobalVarInitialExpr();
	return (new (this) GlobalVarExpr(*g))->evaluateToOriginal();
}

IFDEBUG(void GlobalVarInitialExpr::printTyle() const { printf("%s",(char *)m_name); })

IFDEBUG(void GlobalVarExpr::printTyle() const { printf("%s",(char *)m_v.getName()); })
void GlobalVarExpr::toArray(OList::P array) const
{	if (session.m_annotate)
		ADDSTR("/*g*/");
	ADDSTR(m_v.getName());
}

void GlobalVarInitialExpr::toArray(OList::P array) const
{	if (session.m_annotate)
		ADDSTR("/*g*/");
	ADDSTR(m_name);
}

void StructIndexingExpr::evaluateToCopy(void *space)
{ 
	{
		Object::HolderCP oha;
		a->evaluateToCopy(oha);
		oha.protectChilds();
		if (updateIndex())
		{	oha->indexCopy(m_index.m_index,space);
		}
		else
		{ new (space) NullObject();
			session.m_curTypePtr = 0;
		}
	}
}

Object *StructIndexingExpr::evaluateToOriginal()
{ 
	Object *ao=a->evaluateToOriginal();
	//todo remove more selfpainters?
	if (updateIndex())
		return ao->index(m_index.m_index);
	else { 
		session.m_curTypePtr = 0;
		return NullObject::obj();
	}
}

Object *ParamExpr::evaluateToOriginal()
{ 
	LOG_EXPR;
	Object *ao=DollarBasicExpr::evaluateToOriginal();
	if (updateIndex()) { 
		return ao->index(m_index.m_index);
	}
	else
	{ session.m_curTypePtr = 0;
		return NullObject::obj();
	}
}

Object *HeadStructIndexingExpr::evaluateToOriginal() // use default evaluateToOriginal function instead the one of StructIndexingExpr
{ 
	return Expr::evaluateToCopy();
}

void HeadStructIndexingExpr::evaluateToCopy(void *space)
{ 
	{
		Object::Holder oha;
		a->evaluateToCopy(oha);
		if (updateIndex()) {
			Object::Holder ohHead;
			oha->indexCopy(0,ohHead);
			ohHead->indexCopy(m_index.m_index,space);
		}
		else {
			new (space) NullObject();
			session.m_curTypePtr = 0;
		}
	}
}

void HeadCallExpr::evaluateToCopy(void *space)
{ // a->index(b)
	{
		Object *oha(a->evaluateToPersistCopy());
		TypeVar typeOfA ( session.getCurType() );
		if (updateIndex())
		{ TypeVar *resType=session.m_curTypePtr;
			Object::HolderCP ohHead;
			oha->indexCopy(0,ohHead);
			ohHead.protectChilds();
			{	Object::Holder oh; // no need to CP because parent is already CPed
				ohHead->indexCopy(m_index.m_index,oh);
				Object *ob=b->evaluateToCopy();
				session.pushStack(oha,typeOfA);
				session.pushStack(ob,session.getCurType());
				oh->evaluateToCopy(space);
				session.popStack();
				//todo: clean up garbage
			}
			session.popStack();
			session.m_curTypePtr = resType;
		}
		else
		{	session.m_curTypePtr = 0;
			new (space) NullObject();
		}
	}
#ifdef CHECKGARBAGE_ON_CALL
	ChildrenPainter cpr((Object *)space);
	objectGC.checkGarbage(); 
#endif
}

void HeadStructIndexingExpr::toArray(OList::P array) const
{ ADDA(this->a->toArrayObject(levelHeadIndex));
	ADDSTR("->");
	ADDSTR(m_index.m_name)
}

void HeadCallExpr::toArray(OList::P array) const
{ 
	HeadStructIndexingExpr::toArray(array);
	array=&array->end();
	if ( b->level()!=levelBlock )
	{	OList ol;
		PREPENDSTR_A(" )",ol);
		PREPENDA_A(b->toArrayObject(),ol);
		PREPENDSTR_A("( ",ol);
		ADDA(NEW_OBJ ListObject(ol))
	}
	else
		ADDA(b->toArrayObject())
}

int Expr::level() const { return 0; }
int BlockExpr::level() const { return levelBlock; }
int ConstExpr::level() const
{ int t=m_val->getTypeNum();
	return t==TOT_Null || t==TOT_List || t==TOT_Map ? levelBlock : (t==TOT_Expr ? levelModify : 0);
}

void TypeSelectionExpr::toArray(OList::P array) const
{	ADDA(bracketize(m_func->toArrayObject(m_typeFuncs)));
	if (!!a) ADDA(a->toArrayObject(levelOperand))
}
IFDEBUG( DEFPT(TypeSelection) )
IFDEBUG( 
void ParamExpr::printTyle() const { printf("%s", (char *)m_index.m_name);}
)
void ParamExpr::toArray(OList::P array) const
{	if (session.m_annotate)
		ADDSTR("/*p*/");
	ADDSTR(m_index.m_name)
}

IFDEBUG( 
void SingleParamExpr::printTyle() const 
{ printf("%s", (char *)((SingleParamScope &)*m_scope).m_name);
}
)

void SingleParamExpr::toArray(OList::P array) const
{	if (session.m_annotate)
		ADDSTR("/*p*/");
	ADDSTR(((SingleParamScope &)*m_scope).m_name);
}

IFDEBUG( 
void StructIndexingExpr::printTyle() const { a->printTyle(); printf(" . %s", (char *)m_index.m_name);}
)
void StructIndexingExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR(".")
	ADDSTR(m_index.m_name)
}

void StructIndexingNumberExpr::evaluateToCopy(void *space)
{ 
	a->evaluateToVoid(); //necessary because this can change index
	if (updateIndex())
	{ new (space)  IntegerObject(m_index.m_index);
	}
	else
		new (space) NullObject();
}

void TypeSelectCarrier::assignTypeVars() const
{
	for (List<TypeFunc>::P i(&m_typeFuncs); !!*i;) {
		bool once=false;
		(*i)->evaluate(once);
		if(once)
			i->removeone();
		else
			i++;
	}
}
Object *TypeSelectionExpr::evaluateToOriginal()
{ 
	assignTypeVars();

	if (!!a)  // assignment to variables:
	{ 
		Object *res = a->evaluateToOriginal();
		session.setCurType(m_func->evaluate());
		return res;
	}
	else
	{ session.m_curTypePtr = 0;
		return NullObject::obj();
	}
}
void TypeSelectionExpr::evaluateToCopy(void *space)
{ 
	assignTypeVars();
	if (!!a) 
	{ 
		a->evaluateToCopy(space);
		session.setCurType(m_func->evaluate());
	}
	else
	{	m_func->evaluate(); // can also be, and probably is, a type assignment
		new (space) NullObject();
	}
}
// no TypeSelectionExpr::evaluateToVoid(), because we cannot evaluate 'a' to void anyway

void StructLengthExpr::toArray(OList::P array) const
{
	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR(".@")
}

void StructLengthExpr::evaluateToCopy(void *space)
{
	a->evaluateToVoid();
	TypeVar t(session.getCurType());
	new (space) IntegerObject (!!t?t->m_elems.length():0);
	session.m_curTypePtr = 0;
}

bool StructIndexingExpr::updateIndex()
{
	switch(m_index.update())
	{
	case 0: return true;
	case 1: session.reportRuntimeError(String("Cannot find element '")+m_index.m_name+"' in '"+toTyle()+"'."); break;
	case 2: session.reportRuntimeError(String("Indexing of non-struct in '")+toTyle()+"'."); break;
	}
	return false;
}

bool ParamExpr::updateIndex()
{
	if (m_index.update())
	{	session.reportRuntimeError(String("Cannot find parameter '")+m_index.m_name+"'."); return false;
	}
	return true;
}

IFDEBUG( DEFPT(StructIndexingNumber) )
void StructIndexingNumberExpr::toArray(OList::P array) const
{	ADDA(a->toArrayObject(levelHeadIndex))
	ADDSTR(".@")  
	ADDSTR(m_index.m_name)
}

void SingleParamAssignmentExpr::initEval() const
{
	assignTypeVars();
	if(!!m_func)
		*session.getStackVar(((SingleParamScope &)*m_scope).m_depth).m_typePtr = m_func->evaluate();
	session.execPush(m_scope);
}

void SingleParamAssignmentExpr::evaluateToCopy(void *space)
{
	initEval(); 
	a->evaluateToCopy(space);
	session.execPop();	
}
void SingleParamAssignmentExpr::evaluateToVoid()
{ 
	initEval();
	a->evaluateToVoid();
	session.execPop();	
}

IFDEBUG( 
void ParamSetAssignmentExpr::printTyle() const 
{ printf("<...,...>");
	a->printTyle();
}
)

void ParamSetAssignmentExpr::initEval() const
{
	assignTypeVars();
	TypeVar p(((ParamSetScope &)*m_scope).m_type);
	if (! p->isEmpty() )
		*session.getStackVar(((ParamSetScope &)*m_scope).m_depth).m_typePtr = p;
	session.execPush(m_scope);
}

void ParamSetAssignmentExpr::evaluateToCopy(void *space)
{ initEval();
	a->evaluateToCopy(space);
	session.execPop();
}
void ParamSetAssignmentExpr::evaluateToVoid()
{ initEval();
	a->evaluateToVoid();
	session.execPop();
}

// more sharing
void SingleParamAssignmentExpr::toArray(OList::P array) const
{
	{
		OList a;
		PREPENDSTR_A(" > ",a);
		PREPENDSTR_A(((SingleParamScope &)*m_scope).m_name,a);
		if (!!m_func)
			PREPENDA_A(bracketize(m_func->toArrayObject(m_typeFuncs)),a);
		PREPENDSTR_A("< ",a)
		ADDA(NEW_OBJ ListObject(a));
	}
	ADDA(a->toArrayObject(levelParam));
}
void ParamSetAssignmentExpr::toArray(OList::P array) const
{	
	{	
		OList a;
		PREPENDSTR_A(" >",a);
		PREPENDA_A(((ParamSetScope &)*m_scope).m_type->elemsToArrayObject(m_typeFuncs),a);
		PREPENDSTR_A("< ",a)
		ADDA(NEW_OBJ ListObject(a));
	}
	ADDA(a->toArrayObject(levelParam));
}

#ifdef DEBUGLOGGING
Object *SingleParamExpr::evaluateToOriginal() 
{
	LOG_EXPR;
	return DollarBasicExpr::evaluateToOriginal();
}
#endif

IFDEBUG(void SingleParamAssignmentExpr::printTyle() const { printf("spa\n"); })

IFDEBUG( DEFPT(ClosureVar) )
Object *ClosureVarExpr::evaluateToOriginal() 
{ 
	LOG_EXPR;
	VarValue &v=session.m_curClosures->m_vars[m_index];
	session.m_curTypePtr = v.m_typeVarPtr;
	return v.m_value;
}
void ClosureVarExpr::toArray(OList::P array) const
{
	if (session.m_annotate)
		ADDSTR("/*c*/");
	ADDSTR(((ClosureScope &)*session.m_curClosures->m_scope).m_closures[m_index].name);
}
IFDEBUG(void ClosuredExprExpr::printTyle() const { printf("?closure? "); a->printTyle(); } )

void ClosuredExprExpr::toArray(OList::P array) const
{
	//todo: can this code be removed? or used in finishmakeexpr?
	ADDSTR("? ");
	ClosureBinding *ocs=session.m_curClosures;
	ClosureBindingPtr p;
	new (p) ClosureBinding(m_scope);
	session.m_curClosures=p;
	ADDA(a->toArrayObject(levelModify));
	session.m_curClosures = ocs;
}

void ClosuredExprExpr::evaluateToCopy(void *space) ///.
{ 
	ClosureBindingPtr p;
	new (p) ClosureBinding(a,m_scope);
	new (space) ClosuredExprObject(p );
}

} // namespace

